/*
 * Copyright (c) 2011, Marc-Andre Ferland - madrang@gmail.com
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the The Warrent Team nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *     * This software is free for non-commercial use. You may not use this
 *       software, in whole or in part, in support of any commercial product
 *       without the express consent of the author.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE WARRENT TEAM BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
using System;
using System.Runtime.InteropServices;

namespace OsHelper.UsbNet.LibUsb
{
	/// <summary>
    /// Class representing a Libusb-1.0 session handle.
    /// Session handle are wrapped in a <see cref="System.Runtime.InteropServices.SafeHandle"/>. 
    /// </summary>
    /// <remarks>
    /// <para>The concept of individual Libusb-1.0 sessions allows for your program to use two libraries 
    /// (or dynamically load two modules) which both independently use libusb. This will prevent interference between the 
    /// individual libusb users - for example <see cref="Api.SetDebug"/> will not affect the other 
    /// user of the library, and <see cref="SafeHandle.Close"/> will not destroy resources that the 
    /// other user is still using.</para>
    /// <para>Sessions are created when a new <see cref="SessionHandle"/> instance is created and destroyed through <see cref="SafeHandle.Close"/>.</para>
    /// <para>A <see cref="SessionHandle"/> instance must be created before calling any other <a href="http://libusb.sourceforge.net/api-1.0/index.html">Libusb-1.0 API</a> function.</para>
    /// <para>Session handles are equivalent to a <a href="http://libusb.sourceforge.net/api-1.0/group__lib.html#ga4ec088aa7b79c4a9599e39bf36a72833">libusb_context</a>.</para>
    /// </remarks>
    public class Session : OsHelper.Handles.Handle
    {
		/// <summary>
		/// Creates and initialize a <a href="http://libusb.sourceforge.net/api-1.0/index.html">Libusb-1.0</a> USB session handle.
		/// </summary>
		/// <remarks>
		/// <para>A <see cref="SessionHandle"/> instance must be created before calling any other <a href="http://libusb.sourceforge.net/api-1.0/index.html">Libusb-1.0 API</a> function.</para>
		/// </remarks>
		public Session() : base()
		{
			LibUsb.Error ErrorCode = LibUsb.Error.Success;
			IntPtr NewSession = IntPtr.Zero;
			
			try
			{
				ErrorCode = Session.Init(ref NewSession);
			}
			catch (DllNotFoundException dllNotFound)
			{
				switch (OsHelper.Platform.CurrentOs.Type) {
				case OsHelper.Platform.OsType.Linux:
					throw new DllNotFoundException("libusb-1.0 library not found. " +
					                               "This is often an indication that libusb-1.0 was installed to '/usr/local/lib' and mono.net is not looking for it there. " +
					                               "To resolve this, add the path '/usr/local/lib' to '/etc/ld.so.conf' and run 'ldconfig' as root. (http://www.mono-project.com/DllNotFoundException)",
					                               dllNotFound);
				case OsHelper.Platform.OsType.Windows:
					throw new DllNotFoundException("libusb-1.0.dll not found. " +
					                               "If this is a 64bit operating system, ensure that the 64bit version of libusb-1.0.dll exists in the '\\Windows\\System32' directory.",
					                               dllNotFound);
				default:
					break;
				}
			}
			
			switch (ErrorCode) {
			case LibUsb.Error.Success:
				this.SetHandle(NewSession);
				break;
			default:
				throw new Exception("Unknown error in SessionHandle:Init, LibUsb Code : " + ErrorCode);
			}
		}
		
        protected override bool ReleaseHandle()
        { 
			if (!this.IsInvalid)
            {
				Session.Exit(this.handle);
				this.SetHandleAsInvalid();
			}
			return true;
		}
		
		public enum Levels : int
		{
			/// <summary>
			/// No messages printed by the library.
			/// </summary>
			None = 0,
			
			/// <summary>
			/// Error messages are printed to stderr.
			/// </summary>
			Error = 1,
			
			/// <summary>
			/// Warning and error messages are printed to stderr.
			/// </summary>
			WarningError = 2,
			
			/// <summary>
			/// Informational messages are printed to stdout, warning and error messages are printed to stderr.
			/// </summary>
			All = 3
		}
		
		/// <summary>
		/// Set message verbosity.
		/// </summary>
		/// <remarks>
		/// <para>The default level is 0, which means no messages are ever printed.</para>
		/// <para>If you choose to increase the message verbosity level, ensure that your application does not close the stdout/stderr file descriptors.</para>
		/// <para>You are advised to set level 3. libusb is conservative with its message logging and most of the time, will only log messages that explain error conditions and other oddities. This will help you debug your software.</para>
		/// <para>If the LIBUSB_DEBUG environment variable was set when libusb was initialized, this function does nothing: the message verbosity is fixed to the value in the environment variable.</para>
		/// <para>If libusb was compiled without any message logging, this function does nothing: you'll never get any messages.</para>
		/// <para>If libusb was compiled with verbose debug message logging, this function does nothing: you'll always get messages from all levels.</para>
		/// </remarks>
		public Levels DebugLevel
		{
			set { Session.SetDebug(this, (int)value); }
		}
		
		[DllImport("libusb-1.0.dll", SetLastError = false, EntryPoint = "libusb_init")]
		static internal extern LibUsb.Error Init (ref IntPtr pContext);

		[DllImport("libusb-1.0.dll", SetLastError = false, EntryPoint = "libusb_exit")]
		static internal extern void Exit (IntPtr pContext);
		
		[DllImport("libusb-1.0.dll", SetLastError = false, EntryPoint = "libusb_set_debug")]
		private static extern void SetDebug ([In] Session SessionHandle, int level);
		
		
		//TODO get more info about libusb_strerror
		/*[DllImport("libusb-1.0.dll", SetLastError = false, EntryPoint = "libusb_strerror")]
		private static extern IntPtr StrError (int errcode);*/
    }
}
